/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.sales.generics.client.components.scroll;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author v
 */
public final class HScroll extends Composite implements HasWidgets.ForIsWidget {

    private final FlowPanel scrollPanel = new FlowPanel();
    private final Axis axis;
    private final FlowPanel content;
    private static final String SCROLLBAR_CLASS_NAME = "mCustomScrollbar";
    private final List<ScrollHandler> scrollHandlerList = new ArrayList<>();
    private boolean built;

    /**
     * Creates a HScroll with vertical scrollbar.
     */
    public HScroll() {
        this(Axis.VERTICAL_SCROLLBAR);
    }

    /**
     * Use this constructor to create a HScroll with horizontal or
     * horizontal-vertical scrollbar. If you are using UIBinder, annotate the
     * field with "@UiField(provided = true)" and use this constructor to
     * initialize the scroll panel.
     *
     * @param axis
     */
    public HScroll(Axis axis) {
        this.axis = axis;
        content = new FlowPanel();
        scrollPanel.add(content);
        content.addStyleName("hscroll-content");
        initWidget(scrollPanel);
    }

    /**
     * This method adds a widget in the end of scroll panel.
     *
     * @param widget
     */
    @Override
    public void add(Widget widget) {
        content.add(widget);
    }

    /**
     * This method adds a widget in the end of scroll.
     *
     * @param isWidget
     */
    @Override
    public void add(IsWidget isWidget) {
        content.add(isWidget);
    }

    /**
     * Removes a widget from the component.
     *
     * @param widget
     * @return false if the widget is not present.
     */
    @Override
    public boolean remove(Widget widget) {
        return content.remove(widget);
    }

    /**
     * Removes a IsWidget object from the component.
     *
     * @param isWidget
     * @return false if the widget is not present.
     */
    @Override
    public boolean remove(IsWidget isWidget) {
        return content.remove(isWidget);
    }

    /**
     * Removes all widgets.
     */
    @Override
    public void clear() {
        content.clear();
    }

    /**
     * Returns the interator from the component.
     *
     * @return a iterator with the widgets.
     */
    @Override
    public Iterator<Widget> iterator() {
        return content.iterator();
    }

    /**
     * This method is called when the component is attached on the screen.
     * After, the scroll bar is created through of the JQuery plugin.
     */
    @Override
    protected void onAttach() {
        super.onAttach();
        if (!built) {
            getElement().getStyle().setOverflow(Style.Overflow.HIDDEN);
            if (axis == HScroll.Axis.VERTICAL_SCROLLBAR) {
                getElement().getStyle().setHeight(1, Style.Unit.PX);
                createScrollBar(getElement());
                addStyleName(axis.className);
                getElement().getStyle().setHeight(100, Style.Unit.PCT);
                setVerticalPosition(getElement(), 0);
                built = true;
                return;
            }
            if (axis == HScroll.Axis.HORIZONTAL_SCROLLBAR) {
                createScrollBar(getElement());
                int clientWidth = content.getElement().getFirstChildElement().getClientWidth();
                content.getElement().getParentElement().getStyle().setWidth(clientWidth, Style.Unit.PX);
                int height = getElement().getFirstChildElement().getOffsetHeight();
                getElement().getFirstChildElement().getStyle().setHeight(height - 16, Style.Unit.PX);
                addStyleName(axis.className);
                built = true;
                return;
            }
            if (axis == HScroll.Axis.HORIZONTAL_VERTICAL_SCROLLBAR) {
                getElement().getStyle().setHeight(1, Style.Unit.PX);
                createScrollBar(getElement());
                addStyleName(axis.className);
                getElement().getStyle().setHeight(100, Style.Unit.PCT);
                int clientWidth = content.getElement().getFirstChildElement().getClientWidth();
                content.getElement().getParentElement().getStyle().setWidth(clientWidth, Style.Unit.PX);
                int clientHeight = content.getElement().getFirstChildElement().getClientHeight();
                content.getElement().getParentElement().getStyle().setHeight(clientHeight, Style.Unit.PX);
                setVerticalPosition(getElement(), 0);
                built = true;
            }
        }
    }

    /**
     * Adds a Scrollhandler to the component.
     *
     * @param scrollHandler
     */
    public void addScrollHandler(ScrollHandler scrollHandler) {
        scrollHandlerList.add(scrollHandler);
    }

    /*
     * Shows the element on screen.
     *
     * @param element
     */
    public static void scrollIntoView(Element element) {
        boolean notFound = true;
        Element parentElement = element.getParentElement();
        while (notFound) {
            if (parentElement.getClassName().contains(SCROLLBAR_CLASS_NAME)) {
                scrollToElement(element, parentElement);
                notFound = false;
            }
            if (parentElement.hasParentElement()) {
                parentElement = parentElement.getParentElement();
            } else {
                break;
            }
        }
    }

    /**
     * Scrolls the component to position.
     *
     * @param position
     */
    public void setVerticalScrollPosition(int position) {
        setVerticalPosition(getElement(), position);
    }

    /**
     * Call the methods in the ScrollHandlers.
     */
    void managerCallbacks() {
        for (ScrollHandler scrollHandler : scrollHandlerList) {
            scrollHandler.onTotalScroll();
        }
    }

    private static native void scrollToElement(Element element, Element scroll)/*-{
     $wnd.$(scroll).mCustomScrollbar("scrollTo",element);
     }-*/;

    private native void setVerticalPosition(Element element, int position)/*-{
     $wnd.$(element).mCustomScrollbar("scrollTo",position);
     }-*/;

    private native void createScrollBar(Element elem)/*-{
     var hscroll = this;
     $wnd.$(elem).mCustomScrollbar({
     axis: "yx",
     keyboard:{ enable: false },    
     mouseWheel:{ enable: true },
     mouseWheel:{ scrollAmount: 35 },
     scrollInertia: 0,
     theme: "minimal-dark",
     callbacks:{
     onTotalScroll:function(){
     hscroll.@br.com.sales.generics.client.components.scroll.HScroll::managerCallbacks()();
     }}
     });
     }-*/;

    /**
     * This enum determines if the component will be a vertical,a horizontal or
     * a vertical-horizontal scrollbar.
     */
    public enum Axis {

        HORIZONTAL_SCROLLBAR("hscroll-horizontal-scrollbar"),
        VERTICAL_SCROLLBAR("hscroll-vertical-scrollbar"),
        HORIZONTAL_VERTICAL_SCROLLBAR("hscroll-horizontal-vertical-scrollbar");
        private final String className;

        private Axis(String className) {
            this.className = className;
        }
    }
}
